﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace InverseKinematics
{
    /// <summary>
    /// Generates all the relevant matrices for viewing in 3D space
    /// </summary>
    public class Camera
    {
        /// <summary>
        /// the camera's position
        /// </summary>
        private Vector3 cameraPosition = new Vector3(0.0f, 0.0f, 300.0f);
        
        /// <summary>
        /// where to look at
        /// </summary>
        private Vector3 cameraLookAt = new Vector3(0.0f, 0.0f, 0.0f);

        /// <summary>
        /// the camera's projection matrix
        /// </summary>
        private Matrix cameraProjectionMatrix;
        
        /// <summary>
        /// the camera's view matrix
        /// </summary>
        private Matrix cameraViewMatrix;
        
        /// <summary>
        /// switches the camera's projection to orthografic
        /// </summary>
        private bool orthographic = false;
        
        /// <summary>
        /// 
        /// </summary>
        private float fieldOfView = MathHelper.ToRadians(45.0f);
        
        /// <summary>
        /// clipping for near 3D objects
        /// nearer objects are not drawn
        /// </summary>
        private float nearClip = 1.0f;
        
        /// <summary>
        /// clipping for far 3D objects
        /// farer objects are not drawn
        /// </summary>
        private float farClip = 10000.0f;

        /// <summary>
        /// reference of the graphics device manager for generating the field of view
        /// </summary>
        private GraphicsDeviceManager graphics;
        
        /// <summary>
        /// Creates a camera in 3D space which can look at particular objects in space.
        /// with default attributes(look at 0/0/0, position(0/0/300)
        /// </summary>
        /// <param name="graphics">a GraphicsDeviceManager is needed for generating the fieldofview matrix</param>
        public Camera(GraphicsDeviceManager graphics)
        {
            this.graphics = graphics;
            CreateProjection();
            CreateView();

        }

        /// <summary>
        /// Creates a camera in 3D space which can look at particular objects in space.
        /// </summary>
        /// <param name="graphics">a GraphicsDeviceManager is needed for generating the fieldofview matrix</param>
        /// <param name="position">the camera's position</param>
        /// <param name="lookAt">the coordinate to look at</param>
        public Camera(GraphicsDeviceManager graphics, Vector3 position, Vector3 lookAt)
        {
            cameraPosition = position;
            cameraLookAt = lookAt;
            CreateProjection();
            CreateView();
        }

        /// <summary>
        /// the camera's position
        /// </summary>
        public Vector3 CameraPosition
        {
            set 
            { 
                cameraPosition = value;
                CreateView();
            }
            get 
            {
                return cameraPosition;
            }
        }

        /// <summary>
        /// where to look at
        /// </summary>
        public Vector3 CameraLookAt
        {
            set { 
                cameraLookAt = value;
                CreateView();
            }
        }

        /// <summary>
        /// sets the far clip
        /// </summary>
        public float NearClip
        {
            set
            {
                nearClip = value;
                CreateProjection();
            }
            get
            {
                return nearClip;
            }
        }

        /// <summary>
        /// sets the far clip
        /// </summary>
        public float FarClip
        {
            set
            {
                farClip = value;
                CreateProjection();
            }
        }

        /// <summary>
        /// changes the field of view in degrees when the camera's projection is in perspective mode
        /// </summary>
        public float FieldOfViewDegrees
        {
            set 
            { 
                fieldOfView = MathHelper.ToRadians(value);
                CreateProjection();
            }
        }

        /// <summary>
        /// returns the camera's projection matrix
        /// </summary>
        public Matrix CameraProjectionMatrix
        {
            get { return cameraProjectionMatrix; }
        }

        /// <summary>
        /// returns the camera's view matrix
        /// </summary>
        public Matrix CameraViewMatrix
        {
            get { return cameraViewMatrix; }
        }
        /// <summary>
        /// creates the view matrix
        /// </summary>
        private void CreateView()
        {
            cameraViewMatrix = Matrix.CreateLookAt(cameraPosition, cameraLookAt, Vector3.Up);
        }

        /// <summary>
        /// creates the projection matrix
        /// </summary>
        private void CreateProjection()
        {
            if (orthographic)
                cameraProjectionMatrix = Matrix.CreateOrthographic(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, nearClip, farClip);
            else
                cameraProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                    fieldOfView, graphics.GraphicsDevice.Viewport.AspectRatio, nearClip, farClip);
        }

        
    }
}
